package com.hzb.myvpn.model;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.net.VpnService;
import android.net.VpnService.Builder;
import android.os.ParcelFileDescriptor;
import android.util.Log;

public class AndroidTunDev implements ITunDev {
    static {
        System.loadLibrary("AndroidTunDev");
    }
    
    private ParcelFileDescriptor fd;
    private FileOutputStream out;
    private FileInputStream in;
    private int fdInt;
    
    public static class Factory implements ITunDevFactory {
        private VpnService service;
        public Factory(VpnService service) {
            this.service = service;
        }

        @Override
        public ITunDev createTunDev(ConnInfo connInfo) throws IOException {
            Log.i("Vpn", "Prepare create tun dev");
            Builder builder = service.new Builder();
            builder.addAddress(connInfo.getLocalIp(), 24);
            for (Route route : connInfo.getRoutes()) {
                builder.addRoute(route.getAddress(), route.getPrefix());
            }
            ParcelFileDescriptor fd = builder.establish();
            try {
                Log.i("Vpn", "Finish create tun dev");
                return new AndroidTunDev(fd);
            } catch (Exception e) {
                try {
                    fd.close();
                } catch (IOException e1) {
                }
                throw new IOException(e);
            }
        }
    }
    
    private AndroidTunDev(ParcelFileDescriptor fd) {
        this.fd = fd;
        this.fdInt = fd.getFd();
        this.in = new FileInputStream(fd.getFileDescriptor());
        this.out = new FileOutputStream(fd.getFileDescriptor());
    }
    
    private static native int waitReady(int fd, int timeout);
    
    private void ensureOpened() throws IOException {
        if (this.fd == null) throw new IOException("TunDev not opened");
    }
    
    /* (non-Javadoc)
     * @see com.hzb.myvpn.model.ITunDev#read(byte[], int)
     */
    @Override
    public short read(byte[] buffer, int timeout) throws IOException {
        ensureOpened();
        int nread = this.in.read(buffer);
        if (nread == 0) {
            int ret = waitReady(this.fdInt, timeout);
            switch (ret) {
            case 0:
                nread = 0;
                break;
            case 1:
                nread = this.in.read(buffer);
                break;
            default:
                throw new IOException("waitReady return error: " + ret);
            }
        }
        return (short)nread;
    }
    
    /* (non-Javadoc)
     * @see com.hzb.myvpn.model.ITunDev#write(byte[], int)
     */
    @Override
    public void write(byte[] buffer, int size) throws IOException {
        ensureOpened();
        this.out.write(buffer, 0, size);
    }
    
    /* (non-Javadoc)
     * @see com.hzb.myvpn.model.ITunDev#close()
     */
    @Override
    public void close() throws IOException {
        ensureOpened();
        try {
            try {
                in.close();
            } finally {
                in = null;
                out.close();
            }
        } finally {
            out = null;
            ParcelFileDescriptor fd = this.fd;
            this.fd = null;
            fd.close();
        }
    }
}
